<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Redis主从同步原理详解 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.6;
        }
        .hero {
            background: linear-gradient(135deg, #6e48aa 0%, #9d50bb 100%);
        }
        .card {
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 25px rgba(0,0,0,0.1);
        }
        .highlight {
            background: linear-gradient(90deg, rgba(158, 234, 255, 0.4) 0%, rgba(158, 234, 255, 0) 100%);
            border-left: 4px solid #4fd1c5;
        }
        .mermaid-container {
            background-color: #f8fafc;
            border-radius: 0.5rem;
            padding: 1.5rem;
            margin: 2rem 0;
        }
        .section-divider {
            border: none;
            height: 1px;
            background: linear-gradient(90deg, rgba(79, 209, 197, 0) 0%, rgba(79, 209, 197, 0.6) 50%, rgba(79, 209, 197, 0) 100%);
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <section class="hero text-white py-20 px-4 md:px-0">
        <div class="container mx-auto max-w-5xl">
            <div class="flex flex-col items-center text-center">
                <div class="mb-6">
                    <span class="inline-block px-4 py-1 bg-white bg-opacity-20 rounded-full text-sm font-medium mb-4">Redis高可用</span>
                </div>
                <h1 class="text-4xl md:text-5xl font-bold mb-6 font-serif leading-tight">Redis主从同步原理详解</h1>
                <p class="text-xl md:text-2xl max-w-3xl opacity-90 mb-8">解密Redis如何通过全量同步与增量同步机制保证数据一致性</p>
                <div class="flex space-x-4">
                    <a href="#full-sync" class="px-6 py-3 bg-white text-purple-700 font-medium rounded-lg hover:bg-opacity-90 transition">全量同步</a>
                    <a href="#incremental-sync" class="px-6 py-3 bg-white bg-opacity-20 text-white font-medium rounded-lg hover:bg-opacity-30 transition">增量同步</a>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <div class="container mx-auto max-w-4xl px-4 py-12">
        <!-- Introduction -->
        <section class="mb-16">
            <div class="prose max-w-none">
                <p class="text-lg text-gray-700 leading-relaxed">
                    Redis主从模式是保证Redis高可用的核心机制之一，而主从服务器之间的数据一致性则是这一模式的关键所在。本文将深入剖析Redis主从同步的原理，包括全量同步和增量同步两种机制，以及复制偏移量和心跳机制等核心概念。
                </p>
                <div class="mermaid-container">
                    <div class="mermaid">
                        graph LR
                            A[主节点] -- 全量同步 --> B[从节点]
                            A -- 增量同步 --> B
                            A -- 心跳检测 --> B
                    </div>
                </div>
            </div>
        </section>

        <hr class="section-divider my-12">

        <!-- Full Sync Section -->
        <section id="full-sync" class="mb-16">
            <div class="flex items-center mb-8">
                <div class="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center mr-4">
                    <i class="fas fa-database text-purple-600 text-xl"></i>
                </div>
                <h2 class="text-3xl font-bold font-serif text-gray-800">全量同步</h2>
            </div>
            
            <div class="prose max-w-none">
                <p class="text-gray-700 mb-6">
                    当从节点启动、或者与主节点连接断开较长时间后，从节点需要进行全量同步。在这个过程中，主节点会生成一个 RDB 快照文件，该文件包含当前内存中的数据快照。主节点随后将这个快照文件发送给从节点。从节点接收到快照文件后，会清空自身数据集，然后将主节点的数据加载到自己的内存中。
                </p>
                
                <div class="card bg-white p-6 rounded-xl shadow-md mb-8">
                    <div class="flex items-start mb-4">
                        <div class="flex-shrink-0 mr-3 mt-1">
                            <i class="fas fa-lightbulb text-yellow-500"></i>
                        </div>
                        <div>
                            <h3 class="text-lg font-semibold text-gray-800 mb-2">关键细节</h3>
                            <p class="text-gray-700">
                                如果同步过程中又有新的命令进来，此时主节点会把这个命令放入到缓存区，等发送完RDB快照文件后，继续把缓存区的命令发送给从节点，从节点继续执行命令，从而完成一次全量同步。
                            </p>
                        </div>
                    </div>
                </div>
                
                <figure class="mb-8">
                    <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1705719637435-298a11ee-19f3-4360-81cc-c2caba3b770c.png" alt="Redis全量同步示意图" class="w-full rounded-lg shadow-md">
                    <figcaption class="text-center text-sm text-gray-500 mt-2">Redis全量同步流程示意图</figcaption>
                </figure>
                
                <div class="mermaid-container">
                    <div class="mermaid">
                        sequenceDiagram
                            participant 主节点
                            participant 从节点
                            从节点->>主节点: 请求全量同步
                            主节点->>主节点: 生成RDB快照
                            主节点->>从节点: 发送RDB文件
                            从节点->>从节点: 清空数据
                            从节点->>从节点: 加载RDB数据
                            主节点->>从节点: 发送缓冲区积压命令
                    </div>
                </div>
            </div>
        </section>

        <hr class="section-divider my-12">

        <!-- Incremental Sync Section -->
        <section id="incremental-sync" class="mb-16">
            <div class="flex items-center mb-8">
                <div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center mr-4">
                    <i class="fas fa-sync-alt text-blue-600 text-xl"></i>
                </div>
                <h2 class="text-3xl font-bold font-serif text-gray-800">增量同步</h2>
            </div>
            
            <div class="prose max-w-none">
                <p class="text-gray-700 mb-6">
                    一旦从节点完成全量同步，它会开始接收主节点的增量数据。增量同步的实现依赖于主节点将自身的命令操作记录发送给从节点。每当主节点执行写操作时，它都会将该写操作的命令记录发送给所有从节点。从节点接收到这些命令记录后，按照顺序执行这些命令，从而保持与主节点的数据一致。
                </p>
                
                <figure class="mb-8">
                    <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1705719838330-aa42b4c9-3c4f-4799-bd57-81d6f347cb45.png" alt="Redis增量同步示意图" class="w-full rounded-lg shadow-md">
                    <figcaption class="text-center text-sm text-gray-500 mt-2">Redis增量同步流程示意图</figcaption>
                </figure>
                
                <div class="card bg-white p-6 rounded-xl shadow-md mb-8">
                    <h3 class="text-lg font-semibold text-gray-800 mb-4">增量同步的工作流程</h3>
                    <ol class="space-y-3 text-gray-700">
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-700 font-medium text-sm mr-3 flex-shrink-0">1</span>
                            <span>主节点接收客户端写入命令并执行</span>
                        </li>
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-700 font-medium text-sm mr-3 flex-shrink-0">2</span>
                            <span>主节点将命令记录写入内存缓冲区</span>
                        </li>
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-700 font-medium text-sm mr-3 flex-shrink-0">3</span>
                            <span>主节点将命令发送给所有连接的从节点</span>
                        </li>
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-700 font-medium text-sm mr-3 flex-shrink-0">4</span>
                            <span>从节点接收并执行相同的命令序列</span>
                        </li>
                    </ol>
                </div>
            </div>
        </section>

        <hr class="section-divider my-12">

        <!-- Replication Offset Section -->
        <section class="mb-16">
            <div class="flex items-center mb-8">
                <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center mr-4">
                    <i class="fas fa-ruler-combined text-green-600 text-xl"></i>
                </div>
                <h2 class="text-3xl font-bold font-serif text-gray-800">复制偏移量</h2>
            </div>
            
            <div class="prose max-w-none">
                <p class="text-gray-700 mb-6">
                    在同步的过程中有个非常重要的概念叫偏移量，同步状态的标识就是复制偏移量。主节点会定期向从节点发送自己的复制偏移量，从节点则会保存这个偏移量。这使得主节点知道从节点同步到了哪个位置，而从节点也知道自己需要同步到哪个位置。
                </p>
                
                <div class="highlight p-6 rounded-lg mb-8">
                    <p class="text-gray-800 font-medium">
                        参与复制的主从节点都会维护自身的复制偏移量，主节点在处理完写入命令后，会把命令的字节长度做累加记录，统计在info relication的master_repl_offset指标中。从节点每秒钟上报自身的复制偏移量给主节点，因此主节点也会保存从节点的复制偏移量。从节点在接收到主节点发送的命令后，也会累加记录自身的偏移量，统计在info relication的slave_repl_offset指标中。
                    </p>
                </div>
                
                <figure class="mb-8">
                    <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1705720424566-53ebf88f-e194-48e8-ac88-28ec3a335e3e.png" alt="Redis复制偏移量示意图" class="w-full rounded-lg shadow-md">
                    <figcaption class="text-center text-sm text-gray-500 mt-2">Redis复制偏移量概念示意图</figcaption>
                </figure>
                
                <div class="mermaid-container">
                    <div class="mermaid">
                        flowchart TD
                            A[主节点执行命令] --> B[增加master_repl_offset]
                            B --> C[发送命令到从节点]
                            C --> D[从节点执行命令]
                            D --> E[增加slave_repl_offset]
                            E --> F[定期上报偏移量]
                            F --> A
                    </div>
                </div>
            </div>
        </section>

        <hr class="section-divider my-12">

        <!-- Heartbeat Mechanism Section -->
        <section class="mb-16">
            <div class="flex items-center mb-8">
                <div class="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center mr-4">
                    <i class="fas fa-heartbeat text-red-600 text-xl"></i>
                </div>
                <h2 class="text-3xl font-bold font-serif text-gray-800">心跳机制</h2>
            </div>
            
            <div class="prose max-w-none">
                <p class="text-gray-700 mb-6">
                    为了保持连接，主从节点之间采用心跳机制。主节点周期性地向从节点发送 PING 命令，而从节点则回复 PONG。如果主节点在一定时间内没有收到从节点的 PONG 回复，它会认为从节点不可用，并触发重新连接或者其他处理机制。
                </p>
                
                <figure class="mb-8">
                    <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1705723700142-3600af5b-7a34-42be-8b90-04abe3474fec.png" alt="Redis心跳机制示意图" class="w-full rounded-lg shadow-md">
                    <figcaption class="text-center text-sm text-gray-500 mt-2">Redis心跳检测机制示意图</figcaption>
                </figure>
                
                <div class="grid md:grid-cols-2 gap-6 mb-8">
                    <div class="card bg-white p-6 rounded-xl shadow-md">
                        <h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                            <i class="fas fa-server text-gray-500 mr-2"></i> 主节点行为
                        </h3>
                        <ul class="space-y-2 text-gray-700">
                            <li class="flex items-start">
                                <i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-sm"></i>
                                <span>定期发送PING命令(默认10秒)</span>
                            </li>
                            <li class="flex items-start">
                                <i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-sm"></i>
                                <span>检测从节点是否在线</span>
                            </li>
                            <li class="flex items-start">
                                <i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-sm"></i>
                                <span>超时未响应则标记为下线</span>
                            </li>
                        </ul>
                    </div>
                    <div class="card bg-white p-6 rounded-xl shadow-md">
                        <h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                            <i class="fas fa-laptop-code text-gray-500 mr-2"></i> 从节点行为
                        </h3>
                        <ul class="space-y-2 text-gray-700">
                            <li class="flex items-start">
                                <i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-sm"></i>
                                <span>响应主节点的PING命令</span>
                            </li>
                            <li class="flex items-start">
                                <i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-sm"></i>
                                <span>定期上报复制偏移量</span>
                            </li>
                            <li class="flex items-start">
                                <i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-sm"></i>
                                <span>处理连接断开后的重连</span>
                            </li>
                        </ul>
                    </div>
                </div>
                
                <div class="mermaid-container">
                    <div class="mermaid">
                        stateDiagram-v2
                            [*] --> 主节点在线
                            主节点在线 --> 发送PING: 每10秒
                            发送PING --> 等待PONG: 发送PING
                            等待PONG --> 主节点在线: 收到PONG
                            等待PONG --> 从节点下线: 超时未响应
                            从节点下线 --> 尝试重连: 从节点恢复
                            尝试重连 --> 主节点在线: 重连成功
                    </div>
                </div>
            </div>
        </section>

        <!-- Summary Section -->
        <section class="bg-gray-50 p-8 rounded-xl mb-16">
            <div class="text-center mb-6">
                <i class="fas fa-clipboard-check text-4xl text-purple-600 mb-4"></i>
                <h2 class="text-2xl font-bold font-serif text-gray-800">Redis主从同步核心要点总结</h2>
            </div>
            
            <div class="grid md:grid-cols-2 gap-6">
                <div class="card bg-white p-6 rounded-xl shadow-md">
                    <h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                        <i class="fas fa-check-circle text-purple-600 mr-2"></i> 同步机制
                    </h3>
                    <ul class="space-y-3 text-gray-700">
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-purple-100 text-purple-700 text-xs font-medium mr-3 flex-shrink-0">1</span>
                            <span><strong>全量同步</strong>：从节点初始化或长时间断开后使用RDB快照完整同步数据</span>
                        </li>
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-purple-100 text-purple-700 text-xs font-medium mr-3 flex-shrink-0">2</span>
                            <span><strong>增量同步</strong>：基于命令传播保持主从数据实时一致</span>
                        </li>
                    </ul>
                </div>
                
                <div class="card bg-white p-6 rounded-xl shadow-md">
                    <h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
                        <i class="fas fa-cogs text-purple-600 mr-2"></i> 核心组件
                    </h3>
                    <ul class="space-y-3 text-gray-700">
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-purple-100 text-purple-700 text-xs font-medium mr-3 flex-shrink-0">1</span>
                            <span><strong>复制偏移量</strong>：记录同步进度，用于检测数据一致性和恢复</span>
                        </li>
                        <li class="flex items-start">
                            <span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-purple-100 text-purple-700 text-xs font-medium mr-3 flex-shrink-0">2</span>
                            <span><strong>心跳机制</strong>：保持连接健康检测，处理网络异常</span>
                        </li>
                    </ul>
                </div>
            </div>
        </section>
    </div>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-12">
        <div class="container mx-auto px-4">
            <div class="flex flex-col items-center text-center">
                <h3 class="text-xl font-medium text-white mb-2">技术小馆</h3>
                <p class="mb-6">探索技术原理，分享开发经验</p>
                <a href="http://www.yuque.com/jtostring" class="text-blue-400 hover:text-blue-300 transition duration-300">
                    <i class="fas fa-globe mr-2"></i> http://www.yuque.com/jtostring
                </a>
            </div>
        </div>
    </footer>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            },
            securityLevel: 'loose'
        });
        
        // Smooth scrolling for anchor links
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                document.querySelector(this.getAttribute('href')).scrollIntoView({
                    behavior: 'smooth'
                });
            });
        });
    </script>
</body>
</html>